//////////////////////////////////////////////////////////////////
// MattiseOS Kernel
// Date: 2008
// Author: Matthew Iselin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////

// Includes
#include <mattise.h>
#include <stdint.h>
#include <core/dts.h>
#include <mem/util.h>

// Prototypes

// loads the GDTR and flushes segment registers
void gdt_flush( uint32_t ptr );

// Variables

// the GDT
static struct gdt_entry kGdt[32];

// the GDT pointer
static struct gdt_ptr kGdtPtr;

// Functions

// sets a gate in the GDT
void kGdtSetGate( uint32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran )
{
	// check the number given - because it's unsigned we know it'll never be lower than 0
	if( num >= 32 )
		return;

	// set the base first
	kGdt[num].base_lo = base & 0xFFFF;
	kGdt[num].base_mid = (base >> 16) & 0xFF;
	kGdt[num].base_hi = (base >> 24) & 0xFF;

	// set the limit next - part of it sits in the granularity field
	kGdt[num].limit_lo = limit & 0xFFFF;
	kGdt[num].gran = (limit >> 16) & 0x0F;

	// setup the actual granularity
	kGdt[num].gran |= gran & 0xF0;

	// and fiinally, setup the acess bits
	kGdt[num].access = access;
}

// initializes the GDT
void kInitGDT()
{
	// setup the GDT pointer
	kGdtPtr.limit = (sizeof( struct gdt_entry ) * 32) - 1;
	kGdtPtr.base = (uint32_t) &kGdt;

	// map in the GDT with user privileges
	MapPage( 0xFFFFF000, (uint32_t) kGdt, (uint32_t) kGdt, PF_PRESENT | PF_READWRITE | PF_USER | PF_KERNELPAGE | PF_IMMUTABLE );

	// zero the GDT
	memset( kGdt, 0, sizeof( struct gdt_entry ) * 32 );

	// 0x00: null GDT entry
	kGdtSetGate( 0, 0, 0, 0, 0 );

	// 0x08: kernel code segment
	kGdtSetGate( 1, 0, 0xFFFFFFFF, 0x9A, 0xCF );

	// 0x10: kernel data segment
	kGdtSetGate( 2, 0, 0xFFFFFFFF, 0x92, 0xCF );

	// 0x18: userspace code segment
	kGdtSetGate( 3, 0, 0xFFFFFFFF, 0xFA, 0xCF );

	// 0x20: userspace data segment
	kGdtSetGate( 4, 0, 0xFFFFFFFF, 0xF2, 0xCF );
}

// finishes GDT setup
void kFinishGDTSetup()
{
	// flush the GDT
	gdt_flush( (uint32_t) &kGdtPtr );
}
